import { Injectable } from '@angular/core';
import { BehaviorSubject, distinctUntilChanged, map, Observable, skip } from 'rxjs';
import { FormControlStatus, ValidationErrors } from '@angular/forms';

@Injectable({
  providedIn: 'root'
})
export class PaginationService {

  constructor() {
    console.log('PaginationService is created');
  }

  createPagination<S extends { [key: string]: any }>(
    params?: {
      pageParams?: Partial<PageParams>;
      searchParams?: S;
      options?: {
        noPage: boolean
      }
    }
  ): Pagination<S> {

    return new Pagination<S>(params)
  }
}


export class Pagination<S extends { [key: string]: any }> {
  change$: Observable<{ pageParams?: PageParams; searchParams?: S }>;
  pageSize$: Observable<number | undefined>;
  pageNum$: Observable<number | undefined>;

  private filter$: BehaviorSubject<{ pageParams?: PageParams; searchParams?: S }>;
  private searchParams$: BehaviorSubject<SearchModel<S>> | null = null;
  private _pageParams: PageParams | null = {
    pageSize: 10,
    pageNum: 0,
  };
  private hasSearch = false;
  constructor(
    params?: {
      pageParams?: Partial<PageParams>;
      searchParams?: S;
      options?: {
        noPage: boolean
      }
    }
  ) {
    if (params?.searchParams) {
      const searchParams: { [key: string]: any } = {};
      Object.keys(params.searchParams).forEach(key => {
        searchParams[key] = params.searchParams![key];
      });
      this.searchParams$ = new BehaviorSubject<SearchModel<S>>({ search: createFormModel(searchParams) as FormModelState<S> });

      this.searchParams$.pipe(skip(1)).subscribe(changedParams => {
        this.search(changedParams?.search?.model);
      });
      this.hasSearch = true;
    }

    if (params?.options?.noPage === true) {
      this._pageParams = null
    } else if (params?.pageParams) {
      this._pageParams = {
        pageSize: 10,
        pageNum: 0,
        ...params.pageParams,
      };
    }

    this.filter$ = new BehaviorSubject<{ pageParams?: PageParams; searchParams?: S }>({
      pageParams: this._pageParams ? { ...this._pageParams } : undefined,
      searchParams: this?.searchValue ? { ...this.searchValue } : undefined,
    });

    this.pageSize$ = this.filter$.pipe(map(filter => filter.pageParams?.pageSize));
    this.pageNum$ = this.filter$.pipe(map(filter => filter.pageParams?.pageNum));
    this.change$ = this.filter$.pipe(distinctUntilChanged(this.paramsHasChanged));

  }

  paramsHasChanged(pre: any, cur: any): boolean {
    const preKeys = Object.keys(pre);
    const curKeys = Object.keys(cur);
    if (preKeys.length !== curKeys.length || !preKeys.every(k => curKeys.includes(k))) {
      throw Error('两次参数的键值对数量不一致');
    } else {
      return preKeys.some(key => pre[key] !== cur[key]);
    }
  }

  destroy(): void {
    this.searchParams$?.complete();
    this.filter$.complete();
  }
  changePageSize(size: number): void {
    if (this._pageParams === null) {
      throw Error('该Pagination实例没有分页参数，如要修改，不要在构造函数中传入options.noPage为true');
    }
    this._pageParams.pageSize = size;
    this.search({ pageSize: size });
  }

  changePageNum(num: number): void {
    if (this._pageParams === null) {
      throw Error('该Pagination实例没有分页参数，如要修改，不要在构造函数中传入options.noPage为true');
    }
    this._pageParams.pageNum = num;
    this.search({ pageNum: num });
  }


  search(params: any): void {

    const oldParams = this.getCurrentParams()

    const { pageSize, pageNum, ...newSearchParams } = params;

    const result: any = {};

    if (this._pageParams) {
      result.pageParams = { ...oldParams.pageParams }

      if (pageNum !== undefined) {
        result.pageParams.pageNum = pageNum
      }

      if (pageSize !== undefined) {
        result.pageParams.pageSize = pageSize
      }
    }

    if (this.hasSearch) {
      result.searchParams = { ...oldParams.searchParams };

      if (newSearchParams) {
        result.searchParams = {
          ...result.searchParams,
          ...newSearchParams
        }
      }
    }
    console.log("🚀 ~ file: pagination.service.ts:137 ~ Pagination<S ~ search ~ result:", result);

    this.filter$.next(result);
  }




  getCurrentParams(): { pageParams?: PageParams, searchParams?: S } {

    const params: any = {};

    const value = this.filter$.getValue()

    if (value.pageParams) {
      params.pageParams = { ...value.pageParams }
    }

    if (value.searchParams) {
      params.searchParams = { ...value.searchParams }
    }

    return params;
  }

  get snapshot(): { search: FormModelState<S> } | null {
    return this.searchParams$?.getValue() ?? null;
  }

  get searchValue(): S | null {
    return this.snapshot?.search.model ?? null
  }

}

type PageParams = {
  pageSize: number;
  pageNum: number;
};

export function createFormModel<T>(
  model: T,
  options: {
    dirty: boolean | null,
    status: FormControlStatus | null,
    errors: ValidationErrors | null;
  } = {
      dirty: null,
      status: null,
      errors: null
    }
): FormModelState<T> {
  return {
    model,
    dirty: options.dirty,
    status: options.status,
    errors: options.errors
  }
}

type SearchModel<S> = { search: FormModelState<S> }

export interface FormModelState<T> {
  model: T | null;
  dirty: boolean | null;
  status: FormControlStatus | null;
  errors: ValidationErrors | null;
}
